﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public class CarControl : MonoBehaviour {
    internal enum CarDriveType
    {
        FrontWheelDrive,//前驱  
        RearWheelDrive,//后驱  
        FourWheelDrive//四驱  
    }

    internal enum SpeedType
    {
        KPH//千米/h  
    }  
  
    //[SerializeField]是为了成员变量可以在检视面板上显示  
    [SerializeField]
    private CarDriveType m_CarDriveType = CarDriveType.FourWheelDrive; //四驱  
    [SerializeField]
    private WheelCollider[] m_WheelColliders = new WheelCollider[4]; //存放轮子的数组  
    [SerializeField]
    private GameObject[] m_WheelMeshes = new GameObject[4]; //车轮模型  
    [SerializeField]
    private Vector3 m_CentreOfMassOffset;//车的重心  
    [SerializeField]
    private float m_MaximumSteerAngle;//最大可转角度  
    [Range(0, 1)]
    [SerializeField]
    private float m_SteerHelper; //初始化为0，1是车被控制面对的方向  
    [Range(0, 1)]
    [SerializeField]
    private float m_TractionControl; //0没有牵引力控制系统,1是完整的干扰  
    //所有车轮的扭矩  扭矩越大，加速性能越好；扭矩越小，加速性能越差  
    //如某车的1挡齿比（齿轮的齿数比，本质就是齿轮的半径比）是3，尾牙为4，轮胎半径为0.3米，原扭矩是200Nm的话，最后在轮轴的扭矩就变成200×3×4=2400Nm，  
    //再除以轮胎半径0.3米后，轮胎与地面摩擦的部分就有2400Nm/0.3m=8000N，即800公斤力的驱动力，这就足以驱动汽车了  

    //驱动力公式：驱动力=扭矩×变速箱齿比×主减速器速比×机械效率÷轮胎半径  
    [SerializeField]
    private float m_FullTorqueOverAllWheels=6000;//所有轮胎的扭矩  
    [SerializeField]
    private float m_ReverseTorque;//反向扭矩  

    public float m_MaxHandbrakeTorque=10;//最大刹车扭矩  
    [SerializeField]
    private float m_Downforce = 100f;//下压力  
    [SerializeField]
    private SpeedType m_SpeedType;//速度类型  
    [SerializeField]
    private float m_Topspeed = 120;//最大速度  
    [SerializeField]
    private static int NoOfGears = 5;//档位数  
    [SerializeField]
    private float m_RevRangeBoundary = 1f;//最大滑动距离  
    [SerializeField]
    private float m_SlipLimit;//轮胎下滑给定的阈值  
    [SerializeField]
    private float m_BrakeTorque;//刹车扭矩  

    private Quaternion[] m_WheelMeshLocalRotations;//四元数组存储车轮本地转动数据  
    private Vector3 m_Prevpos, m_Pos;
    private float m_SteerAngle; //转向角  
    private int m_GearNum;//档位数  
    private float m_GearFactor;//挡位因子  
    private float m_OldRotation;//用于转动计算  
    private float m_CurrentTorque;//当前扭矩  
    private Rigidbody m_Rigidbody;//刚体  
    private const float k_ReversingThreshold = 0.01f; //反转阈值  

    //角色信息访问控制，对外接口  
    public float BrakeInput { get; private set; }
    public float CurrentSteerAngle { get { return m_SteerAngle; } }//当前车轮角度  
    public float CurrentSpeed { get { return m_Rigidbody.velocity.magnitude * 3.6f; } }
    public float MaxSpeed { get { return m_Topspeed; } }//最大速度  
    public float AccelInput { get; set; } //加速输入  

    //最大速度赋值
    public float TopSpeed
    {
        set {
            m_Topspeed = value;

        }
        get
        {
            return m_Topspeed;
        }
    }

    private void Awake(){
        m_Rigidbody = GetComponent<Rigidbody>();
    }
    // Use this for initialization  
    //初始化  
    private void Start()
    {
        m_WheelMeshLocalRotations = new Quaternion[4]; //获得车轮的四元数的角度信息  
        for (int i = 0; i < 4; i++)
        {
            m_WheelMeshLocalRotations[i] = m_WheelMeshes[i].transform.localRotation;
        }
        m_WheelColliders[0].attachedRigidbody.centerOfMass = m_CentreOfMassOffset;

        m_MaxHandbrakeTorque = float.MaxValue;

        

        //当前扭矩=全部扭矩-（牵引系数【0-1】*全部扭矩）  
        //设置当前扭矩，初始化的扭矩值跟m_TractionControl大小有关，m_TractionControl决定是否有牵引力，如果m_TractionControl  
        //值为0，则当前扭矩直接就是最大值，如果该值为1，则初始扭矩为0，然后汽车启动慢慢增加扭矩力。建议m_TractionControl数值为0.5  
        m_CurrentTorque = m_FullTorqueOverAllWheels - (m_TractionControl * m_FullTorqueOverAllWheels);//  
    }

    //变档函数  
    private void GearChanging()
    {
        float f = Mathf.Abs(CurrentSpeed / MaxSpeed);
        float upgearlimit = (1 / (float)NoOfGears) * (m_GearNum + 1);
        float downgearlimit = (1 / (float)NoOfGears) * m_GearNum;

        if (m_GearNum > 0 && f < downgearlimit)
        {
            m_GearNum--;
        }

        if (f > upgearlimit && (m_GearNum < (NoOfGears - 1)))
        {
            m_GearNum++;
        }
    }

    //在0-1范围内为值添加一个曲线偏向1的简单函数  
    // simple function to add a curved bias towards 1 for a value in the 0-1 range  
    private static float CurveFactor(float factor)
    {
        return 1 - (1 - factor) * (1 - factor);
    }

    //松开插值的版本，允许值超出范围  
    // unclamped version of Lerp, to allow value to exceed the from-to range  
    private static float ULerp(float from, float to, float value)
    {
        return (1.0f - value) * from + value * to;
    }

    //计算齿轮因素/档位因子  
    private void CalculateGearFactor()
    {
        float f = (1 / (float)NoOfGears);
        //齿轮因素是在当前齿轮的速度范围的正常表示。我们顺利走向“目标”装备因素,所以转速时不要立即调节或向下改变齿轮。  
        //目标齿轮数=（当前速度/最大速度）*最大齿轮数-当前齿轮数  
        //我们要让值平滑地想着目标移动，以保证转速不会在变换档位时突然地上高或者降低  
        //反向差值，通过当前速度的比例值，找当前速度在当前档位的比例位置，得到的值将是一个0~1范围内的值。  
        var targetGearFactor = Mathf.InverseLerp(f * m_GearNum, f * (m_GearNum + 1), Mathf.Abs(CurrentSpeed / MaxSpeed));//反插值，计算比例值（5，10，8）=（8-5）/（10-5）=3/5  
        m_GearFactor = Mathf.Lerp(m_GearFactor, targetGearFactor, Time.deltaTime * 5f);   //从当前档位因子向目标档位因子做平滑差值  
    }


    //运动函数，最重要  
    public void Move(float steering, float accel, float footbrake, float handbrake)
    {
        //保持当前轮胎网格跟随wheelcolliders转动  
        for (int i = 0; i < 4; i++)
        {
            Quaternion quat;//四元数quat,用于旋转  
            Vector3 position;
            m_WheelColliders[i].GetWorldPose(out position, out quat);//获取wheelcolliders的姿势，位置和转向角  
            //设置网格物体的位置和转向角  
            m_WheelMeshes[i].transform.position = position;
            m_WheelMeshes[i].transform.rotation = quat;
        }

        //数值限制
        steering = Mathf.Clamp(steering, -1, 1);//限制steering的值在-1和1之间，以下同上  
        AccelInput = accel = Mathf.Clamp(accel, 0, 1);
        BrakeInput = footbrake = -1 * Mathf.Clamp(footbrake, -1, 0);
        handbrake = Mathf.Clamp(handbrake, 0, 1);

        //设置前轮转向角，0，1为前轮，汽车行驶时也只是控制前轮转向  
        m_SteerAngle = steering * m_MaximumSteerAngle;
        m_WheelColliders[0].steerAngle = m_SteerAngle;
        m_WheelColliders[1].steerAngle = m_SteerAngle;
        //调用角度辅助助手  
        SteerHelper();
        //设置加速/刹车信息到WheelCollider  
        ApplyDrive(accel, footbrake);
        //检查速度范围  
        CapSpeed();
        //设置手刹  
        //.2，3代表后轮  
        if (handbrake > 0f)
        {
            //设置手刹值到后轮，达到减速目的  
            var hbTorque = handbrake * m_MaxHandbrakeTorque;
            m_WheelColliders[2].brakeTorque = hbTorque;
            m_WheelColliders[3].brakeTorque = hbTorque;
        }
        //改变档位  
        GearChanging();
        //施加下压力  
        AddDownForce();
        //牵引力控制系统  
        TractionControl();
    }

    //检查速度范围  
    private void CapSpeed()
    {
        float speed = m_Rigidbody.velocity.magnitude;//将标量速度赋予speed  
        //判断速度类型，  
        switch (m_SpeedType)
        {
            case SpeedType.KPH:
                speed *= 3.6f;
                if (speed > m_Topspeed)
                    m_Rigidbody.velocity = (m_Topspeed / 3.6f) * m_Rigidbody.velocity.normalized;
                break;
        }
    }

    //设置加速/刹车信息到WheelCollider  
    private void ApplyDrive(float accel, float footbrake)
    {

        float thrustTorque;
        switch (m_CarDriveType)
        {
            case CarDriveType.FourWheelDrive:
                thrustTorque = accel * (m_CurrentTorque / 4f);
                for (int i = 0; i < 4; i++)
                {
                    m_WheelColliders[i].motorTorque = thrustTorque;
                }
                break;

            case CarDriveType.FrontWheelDrive:
                thrustTorque = accel * (m_CurrentTorque / 2f);
                m_WheelColliders[0].motorTorque = m_WheelColliders[1].motorTorque = thrustTorque;
                break;

            case CarDriveType.RearWheelDrive:
                thrustTorque = accel * (m_CurrentTorque / 2f);
                m_WheelColliders[2].motorTorque = m_WheelColliders[3].motorTorque = thrustTorque;
                break;

        }

        for (int i = 0; i < 4; i++)
        {
            if (CurrentSpeed > 5 && Vector3.Angle(transform.forward, m_Rigidbody.velocity) < 50f)
            {
                m_WheelColliders[i].brakeTorque = m_BrakeTorque * footbrake;
            }
            else if (footbrake > 0)
            {
                m_WheelColliders[i].brakeTorque = 0f;
                m_WheelColliders[i].motorTorque = -m_ReverseTorque * footbrake;
            }
        }
    }

    //此函数时move函数用于转向角  
    private void SteerHelper()
    {
        for (int i = 0; i < 4; i++)
        {
            WheelHit wheelhit;
            m_WheelColliders[i].GetGroundHit(out wheelhit);
            if (wheelhit.normal == Vector3.zero)
                return; // wheels arent on the ground so dont realign the rigidbody velocity 车轮离地，则不用调整汽车角度了。  
        }

        // this if is needed to avoid gimbal lock problems that will make the car suddenly shift direction  
        //使用四元数避免方向向锁的问题  
        //假如上一次车体Y方向角度比这次小于十度，就根据相差的度数乘以系数m_SteerHelper，得出需要旋转的度数  
        //根据这个度数算出四元数，然后将刚体速度直接旋转这个偏移度数  
        if (Mathf.Abs(m_OldRotation - transform.eulerAngles.y) < 10f)
        {
            var turnadjust = (transform.eulerAngles.y - m_OldRotation) * m_SteerHelper;
            Quaternion velRotation = Quaternion.AngleAxis(turnadjust, Vector3.up);
            m_Rigidbody.velocity = velRotation * m_Rigidbody.velocity;
        }
        m_OldRotation = transform.eulerAngles.y;
    }


    //这个是用来增加下压力，这是用来添加更多的速度控制  
    private void AddDownForce()
    {
        for (int i = 0; i < 4; i++) {

            m_WheelColliders[i].attachedRigidbody.AddForce(-transform.up * m_Downforce *
                                                     m_WheelColliders[0].attachedRigidbody.velocity.magnitude);//Addforce 第一个车轮增加一个力
        }
    }



    //如果汽车轮胎过度滑转，牵引力系统可以控制减少轮胎动力  
    // crude traction control that reduces the power to wheel if the car is wheel spinning too much  
    private void TractionControl()//牵引力控制函数  
    {
        WheelHit wheelHit;
        //判断驱动类型，四轮，还是前后轮，然后调用AdjustTorque函数  
        switch (m_CarDriveType)
        {
            case CarDriveType.FourWheelDrive:
                // loop through all wheels  
                for (int i = 0; i < 4; i++)
                {
                    m_WheelColliders[i].GetGroundHit(out wheelHit);

                    AdjustTorque(wheelHit.forwardSlip);
                }
                break;

            case CarDriveType.RearWheelDrive:
                m_WheelColliders[2].GetGroundHit(out wheelHit);
                AdjustTorque(wheelHit.forwardSlip);

                m_WheelColliders[3].GetGroundHit(out wheelHit);
                AdjustTorque(wheelHit.forwardSlip);
                break;

            case CarDriveType.FrontWheelDrive:
                m_WheelColliders[0].GetGroundHit(out wheelHit);
                AdjustTorque(wheelHit.forwardSlip);

                m_WheelColliders[1].GetGroundHit(out wheelHit);
                AdjustTorque(wheelHit.forwardSlip);
                break;
        }
    }

    //当向前滑动距离超过阈值后，就说明轮胎过度滑转，则减少牵引力，以降低转速。上面函数调用  
    private void AdjustTorque(float forwardSlip)
    {
        if (forwardSlip >= m_SlipLimit && m_CurrentTorque >= 0)
        {
            m_CurrentTorque -= 10 * m_TractionControl;
        }
        else
        {
            m_CurrentTorque += 10 * m_TractionControl;
            if (m_CurrentTorque > m_FullTorqueOverAllWheels)
            {
                m_CurrentTorque = m_FullTorqueOverAllWheels;
            }
        }
    }  
  
}
